package org.elec.mac.fliter;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.elec.mac.constants.Constants;
import org.elec.mac.ehcache.CacheConfiguration;
import org.elec.mac.ehcache.ManageEhCache;
import org.elec.mac.entity.*;
import org.elec.mac.utils.JSONUtil;
import org.elec.mac.utils.WebKit;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.Cache;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Enumeration;
import java.util.List;

@Slf4j
@WebFilter(filterName = "CheckSessionFilter", urlPatterns = "*.do", description = "System Filter")
public class CheckSessionFilter implements Filter {

    protected FilterConfig filterConfig = null;

    @Autowired
    private ManageEhCache manageEhCache;

    @Autowired
    private CacheConfiguration cacheConfiguration;

    @Value("${web.filter.pass}")
    public String filterPass;

    @Value("${web.filter.inter}")
    public String interPass;

    @Value("${web.filter.system}")
    public String systemPass;

    @Value("${web.filter.token}")
    public String tokenPass;

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        this.filterConfig = filterConfig;
        manageEhCache.cacheInit();
    }

    @Override
    @ResponseBody
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest hsrq = (HttpServletRequest) request;
        HttpServletResponse hsrp = (HttpServletResponse) response;
        hsrp.setContentType("text/html; charset=UTF-8");
        hsrp.setHeader("Access-Control-Allow-Origin", "*");
        hsrp.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
        hsrp.setHeader("Access-Control-Max-Age", "3600");
        hsrp.setHeader("Access-Control-Allow-Headers", "x-requested-with");


        Enumeration enu = request.getParameterNames();
        while(enu.hasMoreElements()){
            String paraName=(String)enu.nextElement();
            log.info("request_params_"+paraName+": "+request.getParameter(paraName));
        }
        EhCacheCacheManager manager
                = cacheConfiguration.ehCacheCacheManager(cacheConfiguration.ehCacheManagerFactoryBean());
        SessionUser sessionUser = null;
        String username = null;
        String reqPage = StringUtils.trim(hsrq.getServletPath());
        if(reqPage.startsWith("/")) {
            reqPage = reqPage.substring(1);
        }
        log.info(reqPage);
        if (filterPass(systemPass, reqPage) || filterPass(interPass, reqPage)) {
            chain.doFilter(request, response);
        }
        else if (filterPass(tokenPass, reqPage)) {
            ExecuteResult result = oAuthToken(hsrq, hsrp, manager);
            if (!result.isSuccess()) {
                hsrp.getWriter().write(JSONUtil.bean2json(result));
                return;
            }
            chain.doFilter(request, response);
        }
        else {
            // 网络请求Session验证
            sessionUser = (SessionUser) hsrq.getSession().getAttribute(Constants.SESSION_USER_KEY);
            if (sessionUser == null) {
                this.filterLogout(hsrq, hsrp);
                return;
            }
            // 缓存Session验证
            String token = sessionUser.getToken();
            Cache authorityCache = manager.getCache("authorityCache");
            username = authorityCache.get(token, String.class);
            if (StringUtils.isBlank(username)) {
                this.filterLogout(hsrq, hsrp);
                return;
            }
            Cache userTokenCache = manager.getCache("userTokenCache");
            String userToken = userTokenCache.get(username, String.class);
            // 缓存令牌不一致
            if (!token.equals(userToken)) {
                this.filterLogout(hsrq, hsrp);
                return;
            }
            // 获取用户角色
            Cache userCache = manager.getCache("userCache");
            SysUser sysUser = userCache.get(username, SysUser.class);
            Cache roleUserCache = manager.getCache("roleUserCache");
            Long roleId = roleUserCache.get(sysUser.getId(), Long.class);
            if(roleId == null || roleId == 0) {
                hsrp.sendRedirect("accessDenied.do");
                return;
            }
            // 验证用户菜单、功能权限
            Cache roleMenuCache = manager.getCache("roleMenuCache");
            List<SysMenu> menuList = roleMenuCache.get(roleId, ArrayList.class);
            //
            Cache roleActionCache = manager.getCache("roleActionCache");
            List<SysAction> actionList = roleActionCache.get(roleId, ArrayList.class);
            //
            Long menuId = 0L, actionId = 0L;
            //
            for(SysMenu sysMenu : menuList) {
                if(reqPage.equals(sysMenu.getUrl())) {
                    menuId = sysMenu.getId();
                    break;
                }
            }
            // 该请求不为菜单，再验证是否为功能
            if(menuId == 0L) {
                for(SysAction sysAction : actionList) {
                    if(reqPage.equals(sysAction.getActionUrl())) {
                        actionId = sysAction.getId();
                        break;
                    }
                }
            }
            // 验证失败-菜单、功能不存在
            if(menuId == 0L && actionId == 0L && !filterPass(filterPass, reqPage)) {
                hsrp.sendRedirect("accessDenied.do");
                return;
            }
            //
            chain.doFilter(request, response);
        }
    }

    /**
     * 路径通行证
     * @param filterPath
     * @param reqPage
     * @return
     */
    public boolean filterPass(String filterPath, String reqPage) {
        String[] path = filterPath.split(",");
        if (ArrayUtils.contains(path, reqPage)) {
            return true;
        }
        else if (reqPage.indexOf("/") >= 0 && ArrayUtils.contains(path, reqPage.substring(0, reqPage.indexOf("/")))) {
            return true;
        }
        return false;
    }

    /**
     *
     * @param hsrq
     * @param hsrp
     */
    public void filterLogout(HttpServletRequest hsrq, HttpServletResponse hsrp) throws IOException {
        if(WebKit.isAjax(hsrq)) {
            ExecuteResult result = new ExecuteResult();
            result.setSuccess(false);
            result.setLogout(true);
            hsrp.getWriter().write(JSONUtil.object2json(result));
        }
        else {
            hsrp.sendRedirect("login_out.do");
        }
    }

    /**
     *
     * @param hsrq
     * @param hsrp
     * @return
     */
    public ExecuteResult oAuthToken(HttpServletRequest hsrq, HttpServletResponse hsrp, EhCacheCacheManager manager) {
        ExecuteResult result = new ExecuteResult();
        result.setSuccess(false);
        try {
            Cache oAuthTokenCache = manager.getCache("authTokenCache");
            String access_token = hsrq.getParameter("accesstoken");
            if (StringUtils.isBlank(access_token)) {
                result.setMsg("accesstoken不存在");
                return result;
            }
            OAuthToken oAuthToken = oAuthTokenCache.get(access_token, OAuthToken.class);
            if (oAuthToken == null) {
                result.setMsg("登录失效请重新登录");
                return result;
            }
            Calendar calendar = Calendar.getInstance();
            // 登录超时
            if (calendar.getTimeInMillis() - oAuthToken.getLogin_time() > oAuthToken.getExpires_in()) {
                result.setMsg("登录超时请重新登录");
                return result;
            }
            result.setSuccess(true);
        }
        catch (Exception ex) {
            log.error("权限请求异常", ex);
            result.setSuccess(false);
        }
        return result;
    }

    @Override
    public void destroy() {
        this.filterConfig = null;
    }
}
